import sys
import traceback
from redis import Redis
import json
from app.common.enums.bz_enum import SysType
from app.schemas.risk_schema import FeeRiskSchema
from sqlalchemy.orm import Session
from ..common.enums import algo_types
from app.common.enums.resp_enum import ErrTypes
from app.common import exceptions
from . import sign_util, get_time_now
from app import conf
#from app import utils
from app.models import PayProduct, Merchant, Factor, PayChannel
from app.services.channel import get_channel_fees
from app.common.exceptions import GenericException

import logging
logger = logging.getLogger(__name__)


IP_WHITE_LIST_KEY = 'ip_white_list'


CACHE_KEY = 'card_data'
DAY_EX_SECONDS = 60 * 60 * 24 * 2   # 2天
MONTH_EX_SECONDS = 60 * 60 * 24 * 60 # 2个月\
LIMIT_TRANS_AMOUNT = int(conf.LIMIT_TRANS_AMOUNT)
LIMIT_DAY_AMOUNT = int(conf.LIMIT_DAY_AMOUNT)
LIMIT_MONTH_AMOUNT = int(conf.LIMIT_MONTH_AMOUNT)


class RiskUtil():
    "风控工具"

    algo = ''
    fee_conf: FeeRiskSchema
    channel_code = ''
    channel_fees_dt = {}
    pay_product: PayProduct
    pay_channel: PayChannel
    merchant: Merchant
    factor: Factor
    fee_mode: int
    merc_fee: int
    factor_fee: int

    def __init__(self, db: Session, pay_product: PayProduct, pay_channel: PayChannel,
                 factor: Factor, merchant: Merchant,
                 fee_mode, merc_fee, factor_fee):
        logger.debug('>> init RiskUtil')
        self.fee_conf = conf.FEE_RISK_CONF
        #self.channel_code = channel_code
        self.pay_product = pay_product
        self.pay_channel = pay_channel
        self.factor = factor
        self.merchant = merchant
        self.fee_mode = fee_mode
        self.merc_fee = merc_fee
        self.factor_fee = factor_fee
        #channel_fees = get_channel_fees(db=db, channel_code=pay_channel.pay_channel_code)
        #for fee in channel_fees:
        #    self.channel_fees_dt[fee.fee_mode] = fee.fee_value

    def check_all(self):
        logger.info('校验交易风险')
        #result = self.check_merc_fee_risk()
        r1 = self.check_product_status()
        r2 = self.check_channel_status()
        #r3 = self.check_factor_status()
        #r4 = self.check_merc_status()
        r5 =self.check_merc_fee_risk(fee_mode=self.fee_mode, merc_fee=self.merc_fee, factor_fee=self.factor_fee)
        #if r1 and r2 and r3 and r4 and r5:
        if r1 and r2 and r5:
            logger.info('校验通过')
            return True
        else:
            logger.error('校验不通过')
            raise GenericException(ErrTypes.risk_validation_error)

        
    def check_fee_risk(self, fee, fee_mode):
        conf = self.fee_conf
        result = True
        if fee < conf.g_min:
            return False
            #raise exceptions.GenericException(ErrTypes.fee_validate_fail)
        if fee_mode == 1:
            if fee > conf.g_mode1_max:
                return False
                #raise exceptions.GenericException(ErrTypes.fee_validate_fail)
        return result

    def check_global_fee_risk(self, fee, fee_mode):
        conf = self.fee_conf
        result = True
        if fee_mode == 1:
            if fee > conf.g_mode1_max or fee < conf.g_mode1_min:
                return False
        elif fee_mode == 2:
            if fee > conf.g_mode2_max or fee < conf.g_mode2_min:
                return False
        else:
            logger.error('不支持的费率模式: %s' % fee_mode)
                #raise exceptions.GenericException(ErrTypes.fee_validate_fail)
        return result
    
    def check_merc_fee_risk(self, fee_mode, merc_fee, factor_fee):
        '''商户费率校验'''
        logger.debug('>> check_merc_fee_risk')
        result = True
        if not self.check_global_fee_risk(merc_fee, fee_mode):
            raise GenericException(ErrTypes.fee_validate_fail)
        if merc_fee < factor_fee:
            raise GenericException(ErrTypes.fee_validate_fail)
        return result
    
    def check_factor_fee_risk(self, fee_mode, factor_fee, up_factor_fee):
        '''代理费率校验'''
        logger.debug('>> check_factor_fee_risk')
        result = True
        if self.check_global_fee_risk(factor_fee, fee_mode):
            raise GenericException(ErrTypes.fee_validate_fail)
        if factor_fee > up_factor_fee:
            raise GenericException(ErrTypes.fee_validate_fail)
        return result
    
##################### 状态检测 ####################
    
    def check_product_status(self):
        if self.pay_product.status != 1:
           raise GenericException(ErrTypes.risk_product_unavailable) 
        else:
            return True

    def check_channel_status(self):
        if self.pay_channel.status != 1:
           raise GenericException(ErrTypes.risk_channel_unavailable) 
        else:
            return True

    def check_factor_status(self):
        if self.factor.status != 1:
           raise GenericException(ErrTypes.risk_factor_status_abnormal) 
        else:
            return True

    def check_merc_status(self):
        if self.merchant.status != 1:
           raise GenericException(ErrTypes.risk_merc_status_abnormal) 
        else:
            return True


##################### 交易限额 ####################

def is_in_limit_amount(db: Session, r: Redis, card_no, amount):
    logger.info('>> is_in_limit_amount')
    try:
        if amount > LIMIT_TRANS_AMOUNT:
            raise GenericException(ErrTypes.risk_over_card_trans_limit)
        elif not is_in_limit_day_amount(db, r, card_no, amount):
            raise GenericException(ErrTypes.risk_over_card_day_limit)
        elif not is_in_limit_month_amount(db, r, card_no, amount):
            raise GenericException(ErrTypes.risk_over_card_month_limit)
        else:
            return True
    except GenericException as err:
        #logger.error(err)
        raise GenericException(err.error_type, message=err.message, data=err.data)
    except Exception as err:
        logger.error('单卡限额检查错误')
        exc_type, exc_value, exc_obj = sys.exc_info()
        traceback.print_exception(exc_type,exc_value,exc_obj,limit=3,file=sys.stdout)
        raise GenericException(ErrTypes.trans_create_fail)
    

def is_in_limit_trans_amount(db: Session, r: Redis, amount):
    logger.info('>> is_in_limit_trans_amount')
    if amount > LIMIT_TRANS_AMOUNT:
        raise GenericException(ErrTypes.risk_over_card_trans_limit)
    else:
        return True

def is_in_limit_day_amount(db: Session, r: Redis, card_no, amount):
    logger.info('>> is_in_limit_day_amount')
    trans_date = get_time_now(type=5)
    key = f'{CACHE_KEY}:day:{card_no}:{trans_date}'
    card_data = {}
    data_js = r.get(key)
    if data_js:
        card_data = json.loads(data_js)
        new_amount = card_data.get('amount') + amount
        if new_amount > LIMIT_DAY_AMOUNT:
            logger.error(f'单卡日交易超出限额')
            raise GenericException(ErrTypes.risk_over_card_day_limit)
        else:
            return True
    else:
        return True
    

def is_in_limit_month_amount(db: Session, r: Redis, card_no, amount):
    logger.info('>> is_in_limit_month_amount')
    trans_month = get_time_now(type=6)
    key = f'{CACHE_KEY}:day:{card_no}:{trans_month}'
    card_data = {}
    data_js = r.get(key)
    if data_js:
        card_data = json.loads(data_js)
        new_amount = card_data.get('amount') + amount
        if new_amount > LIMIT_DAY_AMOUNT:
            logger.error(f'单卡月交易超出限额')
            raise GenericException(ErrTypes.risk_over_card_month_limit)
        else:
            return True
    else:
        return True

def is_ip_in_white_list(r: Redis, ip_addr: str, target_sys: str=SysType.OPENAPI.value[0]) -> bool:
    v = r.get(f'{IP_WHITE_LIST_KEY}:{ip_addr}:{target_sys}')
    if v:
        return True
    else:
        return False